
使用最新添加的数组和记录到代码生成器中，您可能会注意到有时生成的代码不能按预期执行。原因是目前为止我们忽略了平台的调用规则。对于同一个程序或库中的一个函数如何调用另一个函数，每个平台都定义了自己的规则。这些规则在应用程序二进制接口(ABI)文档中进行了总结。通常的信息包括:\par

\begin{itemize}
\item 是否使用机器寄存器传递参数?如果是，是哪个?
\item 如何将数组和结构体等聚合体传递给函数?
\item 如何处理返回值?
\end{itemize}

在使用中有各种各样的规则时，在一些平台上，聚合体总是间接传递，是将聚合体的副本放在堆栈上，只将指向该副本的指针作为参数传递。在其他平台上，在寄存器中传递小的聚合体(比如128位或256位宽)，只有超个阈值才使用间接参数传递。有些平台还使用浮点和向量寄存器传递参数，而有些平台要求浮点值在整数寄存器中传递。\par

当然，这些都是很有趣的、很底层的东西。不幸的是，它与LLVM IR相关。这还挺令人惊讶的，毕竟我们在LLVM IR中定义了函数的所有参数的类型!事实证明，这还不够。为了理解这一点，来考虑下复数。有些语言有用于复数的内置数据类型，例如：C99有float\underline{~}Complex(等等)。旧版本的C没有复数类型，但可以很容易地定义struct complex \{float re, im;\}并在该类型上创建算术运算。这两种类型都可以映射到\{float, float \} LLVM IR类型。如果ABI现在声明内置复数类型的值在两个浮点寄存器中传递，但用户定义的聚合体总是间接传递，那么该函数提供的信息不足以让LLVM决定如何传递这个特定参数。不幸的是，我们需要向LLVM提供更多信息，而这些是高度特定于ABI的信息。\par

有两种方法可以将此信息指定给LLVM：参数属性和类型重写。您需要使用的内容取决于目标平台和代码生成器，常用的参数属性如下:\par

\begin{itemize}
\item inreg指定参数在寄存器中传递。
\item byval指定按值传递参数。形参必须是指针类型。指向数据的隐藏副本，并传递给调用的函数。
\item zeroext和signext指定传递的整数值应为零或扩展符号。
\item sret此形参保存一个指向内存的指针，该指针用于从函数返回聚合体类型。
\end{itemize}

虽然所有代码生成器都支持zeroext、signext和sret属性，但只有部分代码生成器支持inreg和byval。可以使用addAttr()方法将属性添加到函数的参数中，例如：要在Arg参数上设置inreg属性，可以调用以下方法:\par

\begin{lstlisting}[caption={}]
Arg->addAttr(llvm::Attribute::InReg);
\end{lstlisting}

要设置多个属性，可以使用llvm::AttrBuilder类。\par

提供附加信息的另一种方法是使用类型重写。使用这种方法，可以隐藏原始类型:\par

\begin{itemize}
\item 拆分参数，例如：可以传递两个浮点参数，而不是传递一个复杂参数。
\item 将参数转换为不同的表示形式，例如：将大小为64位或更小的结构体转换为i64整数。
\end{itemize}

在不改变值位数的情况下，在不同类型之间进行转换，可以使用位转换指令。bitcast指令不会对聚合类型进行操作，但这不是一个限制，因为您可以使用指针。如果一个聚合体为一个具有两个int成员的结构体，在LLVM中表示为类型\{i32, i32 \}，那么这个聚合体可以按以下方式位转换为i64:\par

\begin{tcolorbox}[colback=white,colframe=black]
\%intpoint = bitcast { i32, i32}* \%point to i64*
\end{tcolorbox}

这将把指向结构体的指针转换为指向i64整数值的指针。随后，可以加载该值并将其作为参数传递。必须确保这两种类型具有相同的大小。\par

向参数添加属性或更改类型并不复杂。但是，如何知道需要实现什么呢?首先，应该大致了解目标平台上使用的调用约定，例如：Linux上的ELF ABI记录了每种支持的CPU平台(只要只需要查一下文件)。有关于LLVM代码生成器需求的文档，信息的来源是Clang实现，在\url{https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/TargetInfo.cpp}文件中。这个文件包含所有受支持平台的特定于ABI的操作。\par

本节中，您学习了如何为函数调用生成符合平台ABI的IR。下一节将介绍为类和虚函数创建IR的不同方法。\par























